Table of contents

C#

SDK Files for C#

  • Source: GS5.cs

    Provides provides Object-Oriented programming api to native gsCore.dll.

    You can copy it to your own project to compile as part of your product if you prefer working on source code instead of referencing the prebuilt assembly (GS5.dll).

  • Pre-built Assembly: GS5.dll

    The pre-built assembly which is built from GS5.cs as following:

csc /t:library GS5.cs

You can add reference to it in your .NET project and do not need to add GS5.cs to your project.

The source list here is just for quick online reference, please refer to "How to get the latest SDK?" to get the original SDK files. The SDK/C# files are in "Lang\CSharp" folder.

Preparation

  1. Prepare gsCore lib, license file and parameters;
  2. Copy SDK/CSharp (GS5.cs or GS5.dll) files to your app source directory, add them as part of your app project;

Initialize SDK

Before you can call most SDK apis, you must initialize the SDK first with the project-specific parameters. Depending on how you pass in the license file, there are two methods to initialize the SDK:

  • Initialize SDK with license file
using gs;

// These Information is copied from Project Settings in IDE
public const string productId = "8d11ec62-bfa0-4794-9688-41f8aa04630d";
public const string password = "walml_9282&APNQ&18163";
public const string license_filename = "demo.lic";

//Resolve full path to license file
//Depending where you put the license file, its path can be resolved in different way.
//In this demo we put the license file side by side with the current assembly (the DLL/EXE this class file is compiled in)
string getFullPathToLicenseFile()
{
  return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), license_filename);
}

TGSCore core = TGSCore.getInstance();
bool ok = core.init(productId, getFullPathToLicenseFile(),  password);
if(!ok)
{
  //Something wrong, report error...
  throw new Exception(string.Format("SDK init failure, err_code: {0}, err_msg: {1}", core.LastErrorCode, core.LastErrorMessage));
}

In this demo we put the license file side by side with the current assembly (the DLL/EXE this class file is compiled in) so that the app can locate the license file easily.

  • Initialize SDK with raw license data

Instead of dealing with the license file path, you can also pass the license information as raw data array:

//Here we compile the license file "demo.lic" as "Embedded Resource"  in the exe assembly
//so it can be read as following:
Stream licStm = Assembly.GetExecutingAssembly().GetManifestResourceStream("MyApp.demo.lic");
int len = (int)licStm.Length;
byte[] licData = new byte[len];
licStm.Read(licData, 0, len);

TGSCore core = TGSCore.getInstance();
if (!core.init(ProductID, licData, Password))
{
   //Something wrong, report error...
}

Alternatively, you can also convert raw license file to byte array in C# soruce code

bin2cs

bin2cs: Convert binary file to C# source

  
  Usage: bin2cs  inputfile [ outfile ]
  
  Parameters:

    input: binary data file to convert
    
    outfile: [Optional] the output pascal source file
            if not specified, outfile = input + ".cs" 

The output looks like:

/* GS53_Ne2_test1.lic (645 bytes) */
public readonly byte[] BinaryData = {
        0x47,0x53,0x03,0x00,0x01,0x01,0x0C,0x03,0x00,0x00,0x6B,0x02,0x00,0x00,0x95,0x88,
        0x47,0xB8,0x1E,0x57,0xE0,0x82,0xC1,0x87,0x8D,0x6A,0xDF,0x6F,0xA6,0x14,0x88,0x23,
        0x4B,0x27,0x14,0x60,0xE4,0x3C,0x65,0x17,0x09,0xB9,0x43,0x38,0x45,0x23,0x36,0x67,
    ... };

Query License Information

Get Entity

To query license information, you first need to find the entity objects:

-Enumerate All Entities

 for(int i = 0; i < core.EntityCount; i++){
   TGSEntity entity = core.getEntityByIndex(i);
   //dump entity information...
 }

or in a more C#-friendly style:

 for(int i = 0; i < core.Entities.Count; i++){
   TGSEntity entity = core.Entities[i];
   //dump entity information...
 }

You can also get the specific entity by its entityId:

//EntityId is copied from IDE
private const string entityId = "b15c8ac2-d87c-4483-be77-5a8cbc89a62b";

var entity = core.getEntityById(entityId);

//or in a more C#-friendly style:
var entity = core.Entities[entityId];

If your app has only one entity, let's get it by a single line:

var entity = core.Entities[0];

This is because the entity is the only single entity with index 0.

Query Entity License Status

Given an entity object, we can easily retrieve its properties:

  • Get entity Properties*
string id = entity.Id;
string name = entity.Name;
string description = entity.Description;

  • Test if entity is fully licensed, in trial mode or already expired

If your entity is associated with a trial license model (Expire by Access Times, Expire By Hard Date, Expire By Period, Expire by Duration, Expire by Session Time), you can test the entity's license status as following:

if(entity.isUnlocked()){
  //this entity is already activated / fully purchased.
  //app should run in full featured mode for this entity (game level, app module, etc.)...
  run_in_full_featured_mode();
} else if (entity.isLocked()){
  //this entity is locked or trial already expired.
  //we may pop up info to prompt for purchase.
  cannot_run();
} else {
  //this entity still in trial period or duration
  run_in_trial_mode();
}

If your entity is using License Model Always Lock, then the logic can be simplied as:

if(entity.isUnlocked()){
  //this entity is already activated / fully purchased.
  //app should run in full featured mode for this entity (game level, app module, etc.)...
  run_in_full_featured_mode();
} else {
  //this entity is still locked, we need a license to continue.
  //we may pop up info to prompt for purchase.
  cannot_run();
}

Query License Model Parameters

In the above section, we have detected that an entity is still in trial mode, so in order to display proper license information to end user on app UI, such as tell the game player how many trial time left before the game will stop running, we must be able to get more details of the license model.

  • Get License Object Given an entity, it is quite simple to retrive the license object attached to this entity:
TGSLicense lic = entity.License; //or entity.getLicense()

Now that the license object is ready, we can inspect it in various perspectives:

  • Get License Model Type

As a developer we have designed the license project in SoftwareShield IDE before, so we should have known which kind of license model being used for an entity. However, if you are developing a universal logic to parse any entity, it is helpful to find out the license model type being bundled with the input entity:

string model_id = lic.Id;
string model_name = lic.Name;
string model_description = lic.Description;

For example, for Expire By Period, its LicenseID is "gs.lm.expire.period.1", so by comparing the license id you can figure out the correct license model type to inspect further.

  • Get License Model Parameters

Now we have known which license model to inspect, let's retrieve the license parameters according to its model type:

Expire by Access Times

This license model (Expire by Access Times) has two parameters of int type: "usedTimes" and "maxAccessTimes"

int usedTimes = lic.getParamInt("usedTimes");
int maxAccessTimes = lic.getParamInt("maxAccessTimes");

//How many times left the app can launch in trial mode? 
int how_many_access_left = maxAccessTimes - usedTimes;

Expire By Hard Date

This model (Expire By Hard Date) is a bit complex since it have three different use cases, in this tutorial we only demonstrate the most commonly used one: "Expire After", in this case, the license has a predefined expiry date "timeEnd", the app trial mode expires after this date.

DateTime expiryDateUTC = lic.getParamUTCTime("timeEnd");

//Convert UTC DateTime to local time
DateTime expiryDateLocal = expiryDateUTC.ToLocalTime();

SoftwareShield SDK api always returns Coordinated Universal Time (UTC) time, you might have to convert it to local time to display the expiry date in local time zone.

Expire By Period

This license model (Expire By Period) has a UTC datetime parameter "timeFirstAccess", specify the timestamp the entity is first accessed, and an integer parameter "periodInSeconds", specify the total trial period allowed. The following code tries to figure out when the license will expire and how many trial time left before expire.

//Total trial period in seconds
int periodInSeconds = lic.getParamInt("periodInSeconds");

//Figure out when the entity is first accessed (aka. entity.beginAccess() is first called)
//if never accessed before, the _timeFirstAccess_ parameter won't hold a valid datetime and the api can throw exception.

try {
  //Here we use local time
  DateTime timeFirstAccess = lic.getParamUTCTime("timeFirstAccess").ToLocalTime();
  
  //accessed before, so we can calculate the expiry date
  DateTime expiryDate = timeFirstAccess.AddSeconds( periodInSeconds );
  
  //How many trial time left?
  int timeLeftInSeconds = (int)(expiryDate - DateTime.Now).TotalSeconds();
}catch(Exception ex){
  //Never accessed, the expiry date is still unknown!

  //How many trial time left = full trial period.
  int timeLeftInSeconds = periodInSeconds;
}

The interesting part of the above code is that: if the "timeFirstAccess" does not hold a valid timestamp (the default value is undefined), which occurs when the entity is never accessed before, we cannot deduce the exact expiry date, however we can deduce how many time left before expire.

Expire by Duration

This license model (Expire by Duration) is simple, it just adds up all elapsed trial time cumulatively (in "usedDurationInSeconds") until the total trial time exceeds the pre-defined maximum value ("maxDurationInSeconds"). We cannot figure out the expiry date.

//Total trial period in seconds
int usedDurationInSeconds = lic.getParamInt("usedDurationInSeconds");
int maxDurationInSeconds = lic.getParamInt("maxDurationInSeconds");

//How many trial time left?
int timeLeftInSeconds = maxDurationInSeconds - usedDurationInSeconds;

Expire by Session Time

This license model (Expire by Session Time) allows an entity can always be accessed (never expire) but in a limited time, we can get the session time left but cannot get the expiry date.

int sessionTimeUsed = lic.getParamInt("sessionTimeUsed");
int maxSessionTime = lic.getParamInt("maxSessionTime");

//How many trial time left?
int timeLeftInSeconds = maxSessionTime - sessionTimeUsed;

Activation

There are two methods to activate your app, online and offline.

  • Online Activation

After user've made purchase of your app, you send a serial number to the customer as a proof of valid customer and the key to activate your app.

if(core.isServerAlive()){
  bool success = core.applySN(serial);
}else{
  // license server is not available!
  //optionally fall back to offline activation...
}
  • Offline Activation

In order to do offline activation, we need to go through three steps:

  1. Generate Request Code
string requestCode = core.getDummyRequestCode();

We are using dummy request code because the license server can deduce all valid actions from the customer's serial number.

  1. Get License Code from License Server

You display the request code on UI, asking the customer to contact your support team for a valid license code, or if possible, let the customer uses another machine (or a mobile device) to generate the license code by himself/herself via the app's public App Web Activator.

  1. Apply License Code

Once the user input a license code, we can apply it to the app:

string licenseCode = readLicenseCodeFromUI(); //user input the license code.
bool success = core.applyLicenseCode(licenseCode, serial);

Revoke License

SoftwareShield SDK allows you to revoke serial numbers to license server so that the revoked serial numbers can be reused in another machine or the same machine (after hardware upgrade, system re-install, etc.)

  1. Revoke Single Serial Number
string serialToRevoke;
bool success = core.revokeSN(serialToRevoke);

After successful revoke, all those entities previously unlocked by this serial are locked.

  1. Revoke All Serial Numbers
bool success = core.revokeApp();

After successful revoke, all previously unlocked entities are locked.

Transfer License

Revoke serial number is good for most cases, but there are other scenerios that the serial-based revoke won't work. For example, your license model is based on Pay-as-you-go concept, your app is not fully activated, the serial is used to extend the app's life span, so the unlock-lock based serial-number revoke does not make sense at all.

  • To online transfer the current license information from machine A to machine B

On machine A (sender):

string receiptSN = core.uploadApp();

On machine B (receiver):

bool success = core.applySN(receiptSN);
  • To offline transfer the current license information from machine A to machine B:

On machine A (sender):

string lic_backup = core.exportApp();

On machine B (receiver):

TMovePackage mv = core.createMovePackage( lic_backup );

if(core.isServerAlive()){
  //online import
  bool success = mv.importOnline( serial );
}else{
  //offline import
  string moveRequestCode = mv.getImportOfflineRequestCode();
  
  // the user reads the request code from app UI and gets license code from support team (or via App Web Activator)
  string licenseCode; //user input the license code generated either by support team or web activator
  bool success = mv.importOffline(licenseCode);
}

The workflow seems a little messy, but you do not have to fully support all online/offline cases in your app. The SoftwareShield SDK provides complete apis to support all possible use cases.

How to lock app when the serial number becomes invalid

Sometimes you want to implement a logic to test if a serial number is still valid before app can run at client side. You can disable or delete a serial number at CheckPoint2 license management web portal, as a result you can remote control if the app should run by manipulating the serial number on the server side.

string serial; //the serial issued from app vendor
if(!core.isValidSN( serial )){
  core.lockAllEntities();
  //quit app...
}

Listen to Events

To handle SoftwareShield license events, you can add event handler to interested event:

//Handling App Events:  
core.AppEventHandler += (int eventId) =>
{
  switch ((TEventType)eventId)
  {
    case TEventType.EVENT_APP_BEGIN:
      { //App launches, say hello...
        break;
      }
    case TEventType.EVENT_APP_END:
      {
        //Render Exit-UI
        var entity = core.getEntityByIndex(0);

        if (entity.isUnlocked())
        {
          //activated, do nothing
        }
        else if (entity.isLocked())
        {
          //trial already expired, need license to run
          MessageBox.Show("your trial is expired, please activate now");
        }
        else
        {
          //trial mode...
          MessageBox.Show("your trial will expire in XXX");
        }
        break;
      }
  }
};

//Handling Entity Events
core.EntityEventHandler += (int eventId, TGSEntity entity) =>
{
  switch ((TEventType)eventId)
  {
    case TEventType.EVENT_ENTITY_TRY_ACCESS:
      {
        if (entity.isUnlocked())
        {
          //activated, do nothing
        }
        else if (entity.isLocked())
        {
          //trial already expired, need license to run
          //render_activate_UI(entity);
        }
        else
        {
          //trial mode...
        }
        break;
      }
    case TEventType.EVENT_ENTITY_ACCESS_INVALID:
      {
        //trial expired!
        //render_activate_UI(entity);
        break;
      }
    case TEventType.EVENT_ENTITY_ACCESS_HEARTBEAT:
      {
        //check when the entity will expire, inspect its license parameter to give user feedback  
        //when 5 minutes left before expire...
        break;
      }
  }
};